import time,sys
from jili.core.printlog import print
import datetime
from gm import api
from jili.price import price_gm
from jili.ibot import notic_qywx
from copy import deepcopy
"""
品种:
股票:一,买卖openbuy,closesell,二,融资融券,opensell,closebuy,三,转向融资融券,,opensell,closebuy,四,直接还券还款debit buy sell;五,一级申购:openbuy
基金:一,买卖openbuy,closesell,二,申赎,
期货期权:一,买卖开平openbuy,opensell,closebuy,closesell,二,行权 
openclose:open,close,close_today,close_yesterday,credit:融资 融券,debt(债务直接),
    close_force,primary(一级市场),deal_option:行权;hold:备兑抵押
bs:buy,sell
pricetype:market,limit
order_status:working,part_traded,all_traded,canceled,rejected,submitting
"""

gm2ats_bs={
    api.OrderSide_Buy  : "buy",  # 买开
    api.OrderSide_Sell  : "sell"  # 卖开
}
ats2gm_bs={v: k for k, v in gm2ats_bs.items()}
gm2ats_openclose={
    api.PositionEffect_Open : "open",  # 开仓
    api.PositionEffect_Close : "close",  # 平仓, 具体语义取决于对应的交易所（实盘上期所和上海能源所不适用，上期所和上海能源所严格区分平今平昨，需要用3和4）
    api.PositionEffect_CloseToday : "close_today",  # 平今仓
    api.PositionEffect_CloseYesterday : "close_yesterday"  # 平昨仓(只适用于期货，不适用股票，股票用2平仓)
}
ats2gm_openclose={v: k for k, v in gm2ats_openclose.items()}

def ats2gm_price_type(pricetype):
    if pricetype == "market":
        return api.OrderType_Market
    elif pricetype == "limit" or pricetype == "" or pricetype is None:
        return api.OrderType_Limit
    else:
        return pricetype
def gm2ats_price_type(pricetype):
    if pricetype == api.OrderType_Limit:
        return "limit"
    else:
        return "market"
gm2ats_order_status={
    api.OrderStatus_New : "working",  # 已报
api.OrderStatus_PartiallyFilled : "part_traded",  # 部成
api.OrderStatus_Filled : "all_traded",  # 已成
api.OrderStatus_Canceled : "canceled",  # 已撤
api.OrderStatus_Rejected : "rejected",  # 已拒绝
api.OrderStatus_PendingNew : "submitting",  # 待报
api.OrderStatus_Expired : "rejected",  # 已过期
}
gm2ats_order_statusinfo={
    api.OrderStatus_New : "已报",  # 已报
api.OrderStatus_PartiallyFilled : "部成",  # 部成
api.OrderStatus_Filled : "已成",  # 已成
api.OrderStatus_Canceled : "已撤",  # 已撤
api.OrderStatus_Rejected : "已拒绝",  # 已拒绝
api.OrderStatus_PendingNew : "待报",  # 待报
api.OrderStatus_Expired : "已过期"  # 已过期
# api.OrderStatus_PendingTrigger : "待触发CTP条件单",  # 待触发, CTP条件单
# api.OrderStatus_Triggered : "已触发CTP条件单",  # 已触发, CTP条件单
}
gm2ats_orderinfo={#order_business
api.OrderBusiness_NORMAL:"0:普通交易",                        # 普通交易。默认值为空，以保持向前兼容

api.OrderBusiness_STOCK_BUY:"1:买入",                 # 股票,基金,可转债买入（映射OrderSide_Buy和PositionEffect_Open）
api.OrderBusiness_STOCK_SELL:"2:卖出",	                # 股票,基金,可转债卖出（映射OrderSide_Buy和PositionEffect_Close）

api.OrderBusiness_FUTURE_BUY_OPEN:"10:期货买入开仓",             # 期货买入开仓（映射OrderSide_Buy和PositionEffect_Open）
api.OrderBusiness_FUTURE_SELL_CLOSE:"11:期货卖出平仓",	        # 期货卖出平仓（映射OrderSide_Sell和PositionEffect_Close）
api.OrderBusiness_FUTURE_SELL_CLOSE_TODAY:"12:期货卖出平仓，优先平今" ,    # 期货卖出平仓，优先平今（映射OrderSide_Sell和PositionEffect_CloseToday）
api.OrderBusiness_FUTURE_SELL_CLOSE_YESTERDAY:"13:期货卖出平仓，优先平昨",	# 期货卖出平仓，优先平昨（映射OrderSide_Sell和PositionEffect_CloseYesterday）
api.OrderBusiness_FUTURE_SELL_OPEN:"14:期货卖出开仓",             # 期货卖出开仓（映射OrderSide_Sell和PositionEffect_Open）
api.OrderBusiness_FUTURE_BUY_CLOSE:"15:期货买入平仓", 	        # 期货买入平仓（映射OrderSide_Buy和PositionEffect_Close）
api.OrderBusiness_FUTURE_BUY_CLOSE_TODAY:"16:期货买入平仓，优先平今",       # 期货买入平仓，优先平今（映射OrderSide_Buy和PositionEffect_CloseToday）
api.OrderBusiness_FUTURE_BUY_CLOSE_YESTERDAY:"17:期货买入平仓，优先平昨",	# 期货买入平仓，优先平昨（映射OrderSide_Buy和PositionEffect_CloseYesterday）

# api.OrderBusiness_FUTURE_CTP_CONDITIONAL_ORDER:"18:CTP条件单",                # CTP条件单

api.OrderBusiness_IPO_BUY:"100:新股申购",                     # 新股申购	100

api.OrderBusiness_CREDIT_BOM:"200:融资买入",                  # 融资买入(buying on margin)
api.OrderBusiness_CREDIT_SS:"201:融券卖出",                   # 融券卖出(short selling)
api.OrderBusiness_CREDIT_RSBBS:"202:买券还券",                # 买券还券(repay share by buying share)
api.OrderBusiness_CREDIT_RCBSS:"203:卖券还款",                # 卖券还款(repay cash by selling share)
api.OrderBusiness_CREDIT_DRS:"204:直接还券",                  # 直接还券(directly repay share)

api.OrderBusiness_CREDIT_BOC:"207:担保品买入",                  # 担保品买入(buying on collateral)
api.OrderBusiness_CREDIT_SOC:"208:担保品卖出",                  # 担保品卖出(selling on collateral)
api.OrderBusiness_CREDIT_CI:"209:担保品转入",                   # 担保品转入(collateral in)
api.OrderBusiness_CREDIT_CO:"210:担保品转出",                   # 担保品转出(collateral out)
api.OrderBusiness_CREDIT_DRC:"211:直接还款",	                # 直接还款(directly repay cash)

api.OrderBusiness_ETF_BUY:"301:ETF申购",                     # ETF申购(purchase)
api.OrderBusiness_ETF_RED:"302:ETF赎回",                     # ETF赎回(redemption)
api.OrderBusiness_FUND_SUB:"303:基金认购",	                # 基金认购(subscribing)
api.OrderBusiness_FUND_BUY:"304:基金申购",	                # 基金申购(purchase)
api.OrderBusiness_FUND_RED:"305:基金赎回",	                # 基金赎回(redemption)
api.OrderBusiness_FUND_CONVERT:"306:基金转换",                # 基金转换(convert)
api.OrderBusiness_FUND_SPLIT:"307:基金分拆", 	                # 基金分拆(split)
api.OrderBusiness_FUND_MERGE:"308:基金合并",	                # 基金合并(merge)

api.OrderBusiness_BOND_RRP:"400:债券逆回购", 	                # 债券逆回购
api.OrderBusiness_BOND_CONVERTIBLE_BUY:"401:可转债申购",        # 可转债申购(purchase)
api.OrderBusiness_BOND_CONVERTIBLE_CALL:"402:可转债转股",       # 可转债转股
api.OrderBusiness_BOND_CONVERTIBLE_PUT:"403:可转债回售",        # 可转债回售
api.OrderBusiness_BOND_CONVERTIBLE_PUT_CANCEL:"404:可转债回售撤销",	# 可转债回售撤销
# api.OrderBusiness_OPTION_BUY_OPEN:"500:期权买入开仓",             # 期权买入开仓（映射OrderSide_Buy和PositionEffect_Open）
# api.OrderBusiness_OPTION_SELL_CLOSE:"501:期权卖出平仓",	        # 期权卖出平仓（映射OrderSide_Sell和PositionEffect_Open）
# api.OrderBusiness_OPTION_SELL_OPEN:"502:期权卖出开仓",	        # 期权卖出开仓（映射OrderSide_Sell和PositionEffect_Open）
# api.OrderBusiness_OPTION_BUY_CLOSE:"503:期权买入平仓",	        # 期权买入平仓（映射OrderSide_Buy和PositionEffect_Close）
# api.OrderBusiness_OPTION_COVERED_SELL_OPEN:"504:期权备兑开仓",	# 期权备兑开仓(备兑卖出开仓，只适用认购合约)
# api.OrderBusiness_OPTION_COVERED_BUY_CLOSE:"505:期权备兑平仓",	# 期权备兑平仓(备兑买入平仓，只适用认购合约)
# api.OrderBusiness_OPTION_EXERCISE:"506:期权行权"             # 期权行权
}

class tradetool():
    def __init__(self,investorid,token=None):
        self.fund={}
        self.pos={}
        self.orders=[]
        self.working_orders={}
        self.trades=[]
        self.connect_result=-1
        self.tradertool = api
        self.investorid=investorid
        if token:
            self.tradertool.set_token(token)
        print("tradetool init",investorid)
    def run(self,strategy_id="",filename="",token="",serv_addr=""):
        api.run(strategy_id=strategy_id, filename=filename, mode=api.MODE_LIVE, token=token, serv_addr=serv_addr)
    def login(self):
        self.connect_result = 0
        self.tradertool.set_account_id(self.investorid)
        self.qry_info()
        return self.connect_result
    def get_version(self):
        return self.tradertool.get_version()
    def logout(self):
        api.stop()
        return True
    def cancel_order(self,orderid):
        wait_cancel_orders = [{"account_id":self.investorid,"cl_ord_id":orderid}]
        r=self.tradertool.order_cancel(wait_cancel_orders)
        print("cancel_order",r,wait_cancel_orders[0])
        return r
    def qry_info(self,req={}):
        self.get_fund()
        self.get_pos()
        self.get_orders()
        self.get_trade()
    def deal_order(self,c,orderid=None):
        obj, mkcode = price_gm.code2obj(c["symbol"])
        bs = gm2ats_bs[c["side"]]
        openclose = gm2ats_openclose[c["position_effect"]]
        status = gm2ats_order_status[c["status"]]
        pricetype = gm2ats_price_type(c["order_type"])
        statusmsg = gm2ats_order_statusinfo[c["status"]] + c["ord_rej_reason_detail"]
        orderinfo = gm2ats_orderinfo[c["order_business"]]
        if orderid is None:
            orderid = c["cl_ord_id"]
        order = {"obj": obj, "code": c["symbol"], "mkcode": mkcode, "bs": bs, "openclose": openclose,
                 "orderid": orderid, "sysid": c["cl_ord_id"], "qty": c["target_volume"],
                 "qty1": c["filled_volume"],
                 "price": c["price"], "price1": c["filled_vwap"], "status": status, " statusmsg": statusmsg,
                 "ssid": c["strategy_id"], "orderinfo": orderinfo, "pricetype": pricetype}
        order["qty0"] = c["volume"]
        order["timekey"] = c["created_at"]
        for k, v in c.items():
            if k not in order.keys():
                order[k] = v
        return order
    def get_orders(self):
        """
        属性	类型	说明
        strategy_id	str	策略 ID
        account_id	str	账号 ID
        account_name	str	账户登录名
        cl_ord_id	str	委托客户端 ID，下单生成，固定不变（掘金维护，下单唯一标识）
        order_id	str	委托柜台 ID（系统字段，下单不会立刻生成，委托报到柜台才会生成）
        ex_ord_id	str	委托交易所 ID（系统字段，下单不会立刻生成，委托报到柜台才会生成）
        algo_order_id	str	算法单 ID
        symbol	str	标的代码
        status	int	委托状态 取值参考 OrderStatus
        side	int	买卖方向 取值参考 OrderSide
        position_effect	int	开平标志 取值参考 PositionEffect
        position_side	int	持仓方向 取值参考 PositionSide
        order_type	int	委托类型 取值参考 OrderType
        order_duration	int	委托时间属性
        order_qualifier	int	委托成交属性
        order_business	int	委托业务属性 取值参考 OrderBusiness
        order_style	int	委托风格属性 取值参考 OrderStyle
        ord_rej_reason	int	委托拒绝原因 取值参考 OrderRejectReason
        ord_rej_reason_detail	str	委托拒绝原因描述
        position_src	int	头寸来源（系统字段）
        volume	int	委托量
        price	float	委托价格
        value	int	委托额
        percent	float	委托百分比
        target_volume	int	委托目标量
        target_value	int	委托目标额
        target_percent	float	委托目标百分比
        filled_volume	int	已成量 （一笔委托对应多笔成交为累计值）
        filled_vwap	float	已成均价，公式为(price*(1+backtest_slippage_ratio)) （期货实盘不支持）
        filled_amount	float	已成金额，公式为(filled_volume*filled_vwap) （期货实盘不支持）
        filled_commission	float	已成手续费，（实盘不支持）
        created_at	datetime.datetime	委托创建时间
        updated_at	datetime.datetime	委托更新时间
        :return:
        """
        self.working_orders = {}
        oo=[]
        os=self.tradertool.get_orders()
        if os:
            for c in os:
                if self.investorid!=c["account_id"]:
                    continue
                order=self.deal_order(c)
                oo.append(order)
                if order["qty0"]>0:
                    self.working_orders[order["orderid"]]=order
                    print("working_orders", order)
                else:
                    print("orders", order)
        self.orders=oo
        return oo
    def get_pos(self):
        """
        属性	类型	说明
        account_id	str	账号 ID
        account_name	str	账户登录名
        symbol	str	标的代码
        side	int	持仓方向 取值参考 PositionSide
        volume	int	总持仓量; 如果要得到昨持仓量，公式为 (volume - volume_today)
        volume_today	int	今日买入量
        market_value	float	持仓市值
        vwap	float	持仓均价 new_vwap=((position.vwap * position.volume)+(trade.volume*trade.price))/(position.volume+trade.volume) （实盘时，期货跨天持仓，会自动变成昨结价，仿真是开仓均价）
        vwap_open	float	开仓均价（期货适用，实盘适用）
        vwap_diluted	float	摊薄成本（股票适用，实盘适用）
        amount	float	持仓额 (volume*vwap*multiplier)
        price	float	当前行情价格（回测时值为 0）
        fpnl	float	持仓浮动盈亏 ((price - vwap) * volume * multiplier) （基于效率的考虑，回测模式 fpnl 只有仓位变化时或者一天更新一次,仿真模式 3s 更新一次, 回测的 price 为当天的收盘价） （根据持仓均价计算）
        fpnl_open	float	浮动盈亏（期货适用， 根据开仓均价计算）
        fpnl_diluted	float	摊薄浮动盈亏（股票适用，实盘适用）
        cost	float	持仓成本 (vwap * volume * multiplier * margin_ratio)
        order_frozen	int	挂单冻结仓位
        order_frozen_today	int	挂单冻结今仓仓位(仅上期所和上海能源交易所标的支持)
        available	int	非挂单冻结仓位 ，公式为(volume - order_frozen); 如果要得到可平昨仓位，公式为 (available - available_today(仅适用于期货)
        available_today	int	非挂单冻结今仓位，公式为 (volume_today - order_frozen_today)(仅上期所和上海能源交易所标的支持)
        available_now	int	当前可用仓位 (回测不可用，可以使用volume - volume_today)
        credit_position_sellable_volume	int	可卖担保品数
        created_at	datetime.datetime	建仓时间 （实盘不支持）
        updated_at	datetime.datetime	仓位变更时间 （实盘不支持
        :return:
        """
        position = self.tradertool.get_position(self.investorid)
        poss={}
        if position:
            for c in position:
                if c.volume!=0:
                    obj,mkcode=price_gm.code2obj(c["symbol"])
                    bs=gm2ats_bs[c["side"]]
                    pos = {"obj": obj, "code": c["symbol"],"mkcode":mkcode,"bs":bs,
                             "qty": c["volume"], "qty_avl": c["available"],"qty_yesterday":c["volume"]-c["volume_today"],"qty_today":c["volume_today"],
                             "price": c.vwap, "margin": c["market_value"]}
                    for k, v in c.items():
                        if k not in pos.keys():
                            pos[k] = v
                    if obj not in poss.keys():
                        poss[obj]={}
                    poss[obj]["buy"]=pos
                    print("pos", pos)
        self.pos=poss
        return poss

    def get_fund(self):
        """
        属性	类型	说明
        account_id	str	账号 ID
        account_name	str	账户登录名
        currency	int	币种
        nav	float	总资产
        fpnl	float	浮动盈亏
        frozen	float	持仓占用资金 （仅期货实盘支持，股票实盘不支持）
        order_frozen	float	冻结资金
        available	float	可用资金
        market_value	float	市值
        balance	float	资金余额
        created_at	datetime.datetime	资金初始时间
        updated_at	datetime.datetime	资金变更时间
        Position - 持仓对象
        """
        c= self.tradertool.get_cash(self.investorid)

        fund={"cash":c["available"],"frozen0":c["order_frozen"],"margin":c["market_value"],"balance":c["nav"],"investorid":self.investorid}
        for k,v in c.items():
            if k =="Position":
                continue
            if k not in fund.keys():
                fund[k]=v
        self.fund=fund
        print("fund",fund)
        return fund
    def get_trade(self):
        """
        属性	类型	说明
        strategy_id	str	策略 ID
        account_id	str	账号 ID
        account_name	str	账户登录名
        cl_ord_id	str	委托客户端 ID
        order_id	str	柜台委托 ID
        exec_id	str	交易所成交 ID
        symbol	str	委托标的
        side	int	买卖方向 取值参考 OrderSide
        position_effect	int	开平标志 取值参考 PositionEffect
        order_business	int	委托业务属性 OrderBusiness
        ord_rej_reason	int	委托拒绝原因 取值参考 OrderRejectReason
        ord_rej_reason_detail	str	委托拒绝原因描述
        exec_type	int	执行回报类型 取值参考 ExecType
        price	float	成交价格
        volume	int	成交量
        amount	float	成交金额
        cost	float	成交成本金额（仅期货实盘支持，股票实盘不支持）
        created_at	datetime.datetime	回报创建时间
        :return:
        """
        t=self.tradertool.get_execution_reports()
        trades=[]
        if t:
            for c in t:
                if self.investorid!=c["account_id"]:
                    continue
                tt={"timekey":c["created_at"]}
                for k, v in c.items():
                    tt[k]=v
                trades.append(tt)
                print("trade", tt)
        self.trades=trades
        return trades
    def submit_order(self,order):#报单
        obj=order["obj"]
        mkcode=order.get("mkcode")
        bs=order["bs"]
        openclose=order["openclose"]
        price=order["price"]
        qty=order["qty"]
        pricetype=order.get("pricetype","limit")
        # ssid=order.get("ssid","")
        # orderid=order.get("orderid","")
        # msg=order.get("msg","")
        symbol = price_gm.obj2code(obj, mkcode)
        side=ats2gm_bs[bs]
        order_type=ats2gm_price_type(pricetype)
        position_effect=ats2gm_openclose[openclose]
        r=self.tradertool.order_volume(symbol=symbol, volume=qty, side=side, order_type=order_type,position_effect=position_effect, price=price, trigger_type=0, stop_price=0, order_duration=api.OrderDuration_Unknown, order_qualifier=api.OrderQualifier_Unknown,account=self.investorid)
        print("submit_order",order)
        print("submit_order-order_volume","symbol",symbol, "volume",qty, "side",side, "order_type",order_type,"position_effect",position_effect, "price",price, "trigger_type",0, "stop_price",0, "order_duration",api.OrderDuration_Unknown, "order_qualifier",api.OrderQualifier_Unknown,"account",self.investorid)
        print("submit_order-r",r)
        return r
    def openbuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        print("pos openbuy",timekey,qty,posrate,amount,price,msg,ssid,obj)
        order={"obj":obj,"bs":"buy","openclose":"open","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg}
        r=self.submit_order(order)
        if len(r)==0:
            print("openbuy失败")
            return -1
        else:
            print("openbuy成功,订单编号",r)
            return r[0]["cl_ord_id"]
    def closesell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        print("pos closesell", timekey, qty, posrate, amount, price, msg, ssid, obj)
        order = {"obj": obj, "bs": "sell", "openclose": "close", "price": price, "qty": qty, "pricetype": "limit",
                 "ssid": ssid, "msg": msg}
        r = self.submit_order(order)
        if len(r)==0:
            print("openbuy失败")
            return -1
        else:
            print("openbuy成功,订单编号",r)
            return r[0]["cl_ord_id"]
    def opensell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        print("pos opensell",timekey,qty,posrate,amount,price,msg,ssid,obj)
        order={"obj":obj,"bs":"sell","openclose":"open","price":price,"qty":qty,"pricetype":"limit","ssid":ssid,"msg":msg}
        r=self.submit_order(order)
        if len(r)==0:
            print("openbuy失败")
            return -1
        else:
            print("openbuy成功,订单编号",r)
            return r[0]["cl_ord_id"]
    def closebuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        print("pos closebuy", timekey, qty, posrate, amount, price, msg, ssid, obj)
        order = {"obj": obj, "bs": "buy", "openclose": "close", "price": price, "qty": qty, "pricetype": "limit",
                 "ssid": ssid, "msg": msg}
        r = self.submit_order(order)
        if len(r)==0:
            print("openbuy失败")
            return -1
        else:
            print("openbuy成功,订单编号",r)
            return r[0]["cl_ord_id"]
    def buy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        return self.openbuy(timekey, qty, posrate, amount, price, msg, ssid, obj)
    def sell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None):
        return self.closesell(timekey,qty,posrate,amount,price,msg,ssid,obj)
    def on_order(self,context, c):
        if self.investorid != c["account_id"]:
            return None
        order=self.deal_order(c)
        print("on_order",order)
        if order["qty0"] > 0:
            self.working_orders[order["orderid"]] = order
        isadd=True
        for i in self.orders:
            if i["orderid"]==order["orderid"]:
                i.update(order)
                isadd=False
                break
        if isadd:
            self.orders.append(order)
if  __name__=="__main__":
    import sys
    from jili.core.config import load_config_json

    c = load_config_json(r"E:\qjs\zjhhzc\stoppl\ss\multifactor\pos_calc_live\config.json")
    print("config.json:", c)
    t0=tradetool(c)